home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 722 / 722.xpi / chrome / noscript.jar / content / noscript / noscriptOptions.js < prev    next >
Text File  |  2010-02-12  |  21KB  |  683 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3. NoScript - a Firefox extension for whitelist driven safe JavaScript execution
  4. Copyright (C) 2004-2009 Giorgio Maone - g.maone@informaction.com
  5.  
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. const CC = Components.classes;
  23. const CI = Components.interfaces;
  24.  
  25. var ns = noscriptUtil.service;
  26.  
  27. var nsopt = {
  28.   
  29.  
  30.   dom2: /^(?:http[s]?|file):\/\/([^\.\?\/#,;:\\\@]+(:?\.[^\.\?\/#,;:\\\@]+$|$))/,
  31.   utils: null,
  32.   init: function() {
  33.  
  34.     if(ns.uninstalling) { // this should never happen! 
  35.       window.close();
  36.       return;
  37.     }
  38.     
  39.    
  40.     
  41.     this.utils = new UIUtils(ns);
  42.     this.utils.resumeTabSelections();
  43.     
  44.     abeOpts.init();
  45.      
  46.     var locked = ns.locked;
  47.     for each (widget in ["urlText","urlList", "jsglobal", "addButton", "removeButton", "importButton", "exportButton"]) {
  48.       this[widget] = $(widget);
  49.       if(locked) this[widget].disabled = true;
  50.     }
  51.      // forbid <a ping>
  52.     var pingCbx = $("mozopt-browser.send_pings");
  53.     if(pingCbx.getAttribute("label").indexOf("Allow ") == 0) { 
  54.       pingCbx.setAttribute("label", noscriptUtil.getString("allowLocal", ["<a ping...>"]));
  55.       $("opt-noping")
  56.               .setAttribute("label", noscriptUtil.getString("forbidLocal", ["<a ping...>"]));
  57.     }
  58.     
  59.     this.trustedSites = ns.jsPolicySites.clone();
  60.     this.untrustedSites = ns.untrustedSites.clone();
  61.     this.tempSites = ns.tempSites.clone();
  62.     this.gTempSites = ns.gTempSites.clone();
  63.     this.populateUrlList();
  64.     
  65.     this.jsglobal.checked = ns.jsEnabled;
  66.  
  67.     this.utils.visitCheckboxes(function(prefName, inverse, checkbox, mozilla) {
  68.         try {
  69.           var val = mozilla ? ns.prefService.getBoolPref(prefName) : ns.getPref(prefName);
  70.           checkbox.setAttribute("checked", inverse ? !val : val);
  71.           if(ns.prefService.prefIsLocked(mozilla ? prefName : "noscript." + prefName)) {
  72.             checkbox.setAttribute("disabled", true);
  73.           }
  74.         } catch(ex) {}
  75.       }
  76.     );
  77.     
  78.     this.utils.visitTextboxes(function(prefName, box) {
  79.       box.value = ns.getPref(prefName);  
  80.     });
  81.     
  82.     $("opt-showPermanent").setAttribute("label", noscriptUtil.getString("allowLocal", ["[...]"]));
  83.     $("opt-showTemp").setAttribute("label", noscriptUtil.getString("allowTemp", ["[...]"]));
  84.     $("opt-showDistrust").setAttribute("label", noscriptUtil.getString("distrust", ["[...]"]));
  85.     $("opt-showGlobal").setAttribute("label", noscriptUtil.getString("allowGlobal"));
  86.   
  87.     var notifyHideLabels = noscriptUtil.getString("notifyHide").split("%S");
  88.     $("opt-notify.hide").setAttribute("label", notifyHideLabels[0]);
  89.     $("notifyDelayLabel").setAttribute("value", notifyHideLabels[1]);
  90.     $("notifyDelay").value = ns.getPref("notify.hideDelay", 5);
  91.     
  92.     this.soundChooser.setSample(ns.getPref("sound.block"));
  93.     
  94.     this.autoAllowGroup = new ConditionalGroup(ns, "autoAllow", 0);
  95.     this.toggleGroup = new ConditionalGroup(ns, "toolbarToggle", 3);
  96.     
  97.     var val = ns.getPref("allowHttpsOnly", 0);
  98.     $("sel-allowHttpsOnly").selectedIndex = (val < 0 || val > 2) ? 0 : val;
  99.     
  100.     var shortcut = ns.getPref("keys.toggle");
  101.     if(shortcut) {
  102.       shortcut = shortcut.replace(/VK_([^\.]*).*/g, "$1").replace(/\s+/g, '+').replace(/_/g, ' ');
  103.       var shortcutLabel = $("toolbarToggle-shortcut");
  104.       shortcutLabel.value = "(" + shortcut + ")";
  105.       shortcutLabel.removeAttribute("hidden");
  106.     }
  107.     
  108.     this.utils.syncGroup($("opt-secureCookies"));
  109.     
  110.     this.xssEx = new RegExpController(
  111.         "xssEx", 
  112.         ns.rxParsers.multi,
  113.         ns.getPref("filterXExceptions"));
  114.     this.jarEx = new  RegExpController(
  115.         "jarEx", 
  116.         ns.rxParsers.multi,
  117.         ns.getPref("forbidJarDocumentsExceptions"));
  118.     
  119.     // hide incompatible options
  120.     if(top.opener && top.opener.noscriptOverlay && !top.opener.noscriptOverlay.getNotificationBox()) {
  121.       // Moz/SeaMonkey, no notifications
  122.       $("fx-notifications").setAttribute("hidden", "true");
  123.     }
  124.     
  125.     ["clearClick", "opacizeObject"].forEach(function(c) {
  126.       var pref = ns.getPref(c);
  127.       Array.forEach($(c + "Opts").getElementsByTagName("checkbox"), function(cbx) {        
  128.         cbx.setAttribute("checked", !(pref & parseInt(cbx.getAttribute("value"))) ? "false" : "true");
  129.       });
  130.     });
  131.     
  132.     if (!ns.clearClickHandler.isSupported(document))
  133.       ["clearClickOpts", "opt-clearClick.prompt"].forEach(function(id) {
  134.         $(id).setAttribute("hidden", "true"); // Fx <= 1.5
  135.       });
  136.     
  137.     
  138.     if (!ns.placesSupported) {
  139.       $("opt-placesPrefs").setAttribute("hidden", "true");
  140.     }
  141.     
  142.     if (ns.canSerializeConf) this.initSerializeButtons();
  143.     
  144.     if (ns.smUninstaller) this.initUninstallButton();
  145.     
  146.     // $("policy-tree").view = policyModel;
  147.     window.sizeToContent();
  148.     
  149.     this.addButton.setAttribute("enabled", "false");
  150.     this.removeButton.setAttribute("enabled", "false");
  151.   },
  152.   
  153.   initUninstallButton: function() {
  154.     this.utils.moveButtonsDown("uninstallButton");
  155.   },
  156.   
  157.   uninstall: function() {
  158.     ns.smUninstaller.appUninstall(window);
  159.   },
  160.   
  161.   
  162.   initSerializeButtons: function() {
  163.     this.utils.moveButtonsDown("importConfButton", "exportConfButton");
  164.   },
  165.   
  166.   
  167.   importConf: function() {
  168.     this.chooseFile(
  169.       this.buttonToTitle("importConfButton"),
  170.       "Open",
  171.       function(f) {
  172.         ns.restoreConf(ns.readFile(f)) && nsopt.reload();
  173.       }
  174.     );
  175.   },
  176.   exportConf: function() {
  177.     this.save();
  178.     this.chooseFile(
  179.       this.buttonToTitle("exportConfButton"),
  180.       "Save",
  181.       function(f) {
  182.         ns.writeFile(f, ns.serializeConf(true));
  183.       }
  184.     );  
  185.   },
  186.   
  187.   reset: function() {
  188.     
  189.     if(!noscriptUtil.prompter.confirm(window, 
  190.           noscriptUtil.getString("reset.title"),
  191.           noscriptUtil.getString("reset.warning"))
  192.       ) return;
  193.     
  194.     ns.resetDefaults();
  195.     this.reload();
  196.   },
  197.   
  198.   reload: function() {
  199.     this.utils.persistTabSelections();
  200.     var op = top.opener;
  201.     if(op && op.noscriptUtil) {
  202.       op.setTimeout(function() {
  203.           op.noscriptUtil.openOptionsDialog();
  204.       }, 10);
  205.     }
  206.     window.close();
  207.   },
  208.   
  209.   save: function() {
  210.     this.utils.visitCheckboxes(
  211.       function(prefName, inverse, checkbox, mozilla) {
  212.         if(checkbox.getAttribute("collapsed")!="true") {
  213.           const checked = checkbox.getAttribute("checked") == "true";
  214.           const requestedVal = inverse ? !checked : checked;
  215.           
  216.           if(mozilla) {
  217.             try {
  218.               ns.prefService.setBoolPref(prefName, requestedVal);
  219.             } catch(ex) {}
  220.             return;
  221.           }
  222.           
  223.           const prevVal = ns.getPref(prefName);
  224.           if(requestedVal != prevVal) {
  225.             ns.setPref(prefName, requestedVal);
  226.           }
  227.         }
  228.       }
  229.     );
  230.     
  231.     
  232.     this.utils.visitTextboxes(function(prefName, box) {
  233.       if (box.value != ns.getPref(prefName)) {
  234.         ns.setPref(prefName, box.value);
  235.       }
  236.     });
  237.     
  238.     ["clearClick", "opacizeObject"].forEach(function(c) {
  239.       var pref = 0;
  240.       Array.forEach($(c + "Opts").getElementsByTagName("checkbox"), function(cbx) {
  241.         if (cbx.checked) pref = pref | parseInt(cbx.getAttribute("value"));
  242.       });
  243.       ns.setPref(c, pref);
  244.     });
  245.     
  246.     
  247.     ns.setPref("notify.hideDelay", parseInt($("notifyDelay").value) || 
  248.               ns.getPref("notify.hideDelay", 5));
  249.  
  250.     ns.setPref("sound.block", this.soundChooser.getSample());
  251.     
  252.     this.autoAllowGroup.persist();
  253.     this.toggleGroup.persist();
  254.     
  255.     ns.setPref("allowHttpsOnly", $("sel-allowHttpsOnly").selectedIndex);
  256.     
  257.     var exVal = this.xssEx.getValue();
  258.     if(this.xssEx.validate() || !/\S/.test(exVal)) 
  259.       ns.setPref("filterXExceptions", exVal);
  260.     var exVal = this.jarEx.getValue();
  261.     if(this.jarEx.validate() || !/\S/.test(exVal)) 
  262.       ns.setPref("forbidJarDocumentsExceptions", exVal);
  263.     
  264.     if (this.tempRevoked) {
  265.       ns.resetAllowedObjects();
  266.     }
  267.     
  268.     var global = this.jsglobal.getAttribute("checked") == "true";
  269.     var untrustedSites = this.untrustedSites;
  270.     var trustedSites = this.trustedSites;
  271.     var tempSites = this.tempSites;
  272.     var gTempSites = this.gTempSites;
  273.     
  274.     ns.safeCapsOp(function(ns) {
  275.       if(ns.untrustedSites.sitesString != untrustedSites.sitesString
  276.           || ns.jsPolicySites.sitesString != trustedSites.sitesString
  277.           || ns.tempSites.sitesString != tempSites.sitesString
  278.           || ns.gTempSites.sitesString != gTempSites.sitesString) {
  279.         ns.untrustedSites.sitesString = untrustedSites.sitesString;
  280.         ns.persistUntrusted();
  281.         ns.setPref("temp", tempSites.sitesString);
  282.         ns.setPref("gtemp", gTempSites.sitesString);
  283.         
  284.         ns.setJSEnabled(trustedSites.sitesList, true, true);
  285.       }
  286.       ns.jsEnabled = global;
  287.     });
  288.   },
  289.  
  290.   urlListChanged: function() {
  291.     const selectedItems = this.urlList.selectedItems;
  292.     var removeDisabled = true;
  293.     for(var j = selectedItems.length; j-- > 0;) {
  294.       if(selectedItems[j].getAttribute("disabled") != "true") {
  295.         removeDisabled = false;
  296.         break;
  297.       }
  298.     }  
  299.     this.removeButton.setAttribute("disabled", removeDisabled);
  300.     $("revokeButton")
  301.       .setAttribute("disabled", this.tempRevoked || 
  302.           !(this.tempSites.sitesString || this.gTempSites.sitesString || ns.objectWhitelistLen));
  303.     this.urlChanged();
  304.   },
  305.   
  306.   urlChanged: function() {
  307.     var url = this.urlText.value;
  308.     if(url.match(/\s/)) url = this.urlText.value = url.replace(/\s/g,'');
  309.     var addEnabled = url.length > 0 && (url = ns.getSite(url)) ;
  310.     if(addEnabled) {
  311.       var match = url.match(this.dom2);
  312.       if(match) url = match[1];
  313.       url = this.trustedSites.matches(url);
  314.       if(!(addEnabled = !url)) {
  315.         this.ensureVisible(url);
  316.       }
  317.     }
  318.     this.addButton.setAttribute("disabled", !addEnabled);
  319.   },
  320.   
  321.   notifyHideDelay: {
  322.     onInput: function(txt) {
  323.       if(/\D/.test(txt.value)) txt.value = txt.value.replace(/\D/, "");
  324.     },
  325.     onChange: function(txt) {
  326.       txt.value = parseInt(txt.value) || ns.getPref("notify.hideDelay", 5);
  327.     }
  328.   },
  329.   
  330.   ensureVisible: function(site) {
  331.     var item;
  332.     const ul = this.urlList;
  333.     for(var j = ul.getRowCount(); j-- > 0;) {
  334.       if((item = ul.getItemAtIndex(j)).getAttribute("value") == site) {
  335.         ul.ensureElementIsVisible(item);
  336.       }
  337.     }
  338.   },
  339.   
  340.   populateUrlList: function() {
  341.     const policy = this.trustedSites;
  342.     const sites = this.trustedSites.sitesList;
  343.     const ul = this.urlList;
  344.     for(var j = ul.getRowCount(); j-- > 0; ul.removeItemAt(j));
  345.     const dom2 = this.dom2;
  346.     var site, item;
  347.     var match, k, len;
  348.     var tempSites = this.gTempSites.clone();
  349.     tempSites.add(this.tempSites.sitesList);
  350.     var tempMap = this.tempSites.sitesMap;
  351.     for(j = 0, len = sites.length; j < len; j++) {
  352.       site = sites[j];
  353.       // skip protocol + 2nd level domain URLs
  354.       if((match = site.match(dom2)) && policy.matches(item = match[1])) 
  355.         continue;
  356.       
  357.       item = ul.appendItem(site, site);
  358.       if(ns.isMandatory(site)) { 
  359.         item.setAttribute("disabled", "true");
  360.       }
  361.       item.style.fontStyle = (site in tempMap) ? "italic" : "normal";
  362.     }
  363.     this.urlListChanged();
  364.   },
  365.   
  366.   allow: function() {
  367.     const site = ns.getSite(this.urlText.value);
  368.     this.trustedSites.add(site);
  369.     this.tempSites.remove(site, true, true); // see noscriptService#eraseTemp()
  370.     this.gTempSites.remove(site, true, true);
  371.     
  372.     this.untrustedSites.remove(site, false, !ns.mustCascadeTrust(site, false));
  373.     this.populateUrlList();
  374.     this.ensureVisible(site);
  375.     this.addButton.setAttribute("disabled", "true");
  376.   },
  377.   
  378.   remove: function() {
  379.     const ul = this.urlList;
  380.     const selectedItems = ul.selectedItems;
  381.     var visIdx = ul.getIndexOfFirstVisibleRow();
  382.     var lastIdx = visIdx + ul.getNumberOfVisibleRows();
  383.    
  384.     
  385.     
  386.     
  387.     
  388.     var removed = [];
  389.     for(var j = selectedItems.length; j-- > 0;) {
  390.       if(!ns.isMandatory(site = selectedItems[j].value)) {
  391.         removed.push(site);
  392.       }
  393.     }
  394.     if (!removed.length) return;
  395.     
  396.     this.trustedSites.remove(removed, true); // keepUp
  397.     this.tempSites.remove(removed, true, true); // see noscriptService#eraseTemp()
  398.     this.gTempSites.remove(removed, true, true);
  399.       
  400.       
  401.     if(selectedItems.length == 1) {
  402.       if(removed.length == 1) {
  403.         ul.removeItemAt(ul.getIndexOfItem(selectedItems[0]));  
  404.       }
  405.       return;
  406.     }
  407.     
  408.     // TODO: hide flickering
  409.     this.populateUrlList();
  410.     try {
  411.       var rowCount = ul.getRowCount();
  412.       if(rowCount > lastIdx) {
  413.         ul.scrollToIndex(visIdx);
  414.       } else {
  415.         ul.ensureIndexIsVisible(rowCount - 1);
  416.       } 
  417.     } catch(e) {}
  418.   },
  419.   
  420.   tempRevoked: false,
  421.   revokeTemp: function() {
  422.     this.trustedSites.remove(this.tempSites.sitesList, true, true);
  423.     this.trustedSites.remove(this.gTempSites.sitesList, true, true);
  424.     this.untrustedSites.add(this.gTempSites.sitesList);
  425.     this.trustedSites.add(ns.mandatorySites.sitesList);
  426.     this.tempSites.sitesString = "";
  427.     this.gTempSites.sitesString = "";
  428.     this.tempRevoked = true;
  429.     this.populateUrlList();
  430.   },
  431.   
  432.   _soundChooser: null,
  433.   get soundChooser() {
  434.     return this._soundChooser || 
  435.       (this._soundChooser = 
  436.         new SoundChooser(
  437.         "sampleURL", 
  438.         this.buttonToTitle("sampleChooseButton"),
  439.         ns,
  440.         "chrome://noscript/skin/block.wav"
  441.       ));
  442.   },
  443.   
  444.   
  445.   chooseFile: function(title, mode, callback) {
  446.     try {
  447.      const IFP = CI.nsIFilePicker;
  448.       const fp = CC["@mozilla.org/filepicker;1"].createInstance(IFP);
  449.       
  450.       fp.init(window,title, IFP["mode" + mode]);
  451.       fp.appendFilters(IFP.filterText);
  452.       fp.appendFilters(IFP.filterAll);
  453.       fp.filterIndex = 0;
  454.       fp.defaultExtension = ".txt";
  455.       const ret = fp.show();
  456.       if(ret == IFP.returnOK || 
  457.           ret == IFP.returnReplace) {
  458.         callback.call(nsopt, fp.file);
  459.       }
  460.     } catch(ex) {
  461.       noscriptUtil.prompter.alert(window, title, ex.toString());
  462.     }
  463.   },
  464.   
  465.   
  466.   importExport: function(op) {
  467.     this.chooseFile(
  468.       this.buttonToTitle(op + "Button"),
  469.       op == "import" ? "Open" : "Save",
  470.       this[op + "List"]
  471.     );
  472.   },
  473.   
  474.   importList: function(file) {
  475.     var all = ns.readFile(file).replace(/\s+/g, "\n");
  476.     var untrustedPos = all.indexOf("[UNTRUSTED]");
  477.     if(untrustedPos < 0) {
  478.       this.trustedSites.sitesString += "\n" + all;
  479.     } else {
  480.       this.trustedSites.sitesString += "\n" + all.substring(0, untrustedPos);
  481.       this.untrustedSites.sitesString += all.substring(all.indexOf("\n", untrustedPos + 2));
  482.     }
  483.     this.untrustedSites.remove(this.trustedSites.sitesList, false, true);
  484.     this.populateUrlList();
  485.     return null;
  486.   },
  487.   
  488.   exportList: function(file) {
  489.     ns.writeFile(file, 
  490.       this.trustedSites.sitesList.join("\n") + 
  491.       "\n[UNTRUSTED]\n" +
  492.       this.untrustedSites.sitesList.join("\n")
  493.     );
  494.     return null;
  495.   },
  496.   
  497.   syncNsel: function(cbx) {
  498.     var blockNSWB = $("opt-blockNSWB");
  499.     if(cbx.checked) {
  500.       blockNSWB.disabled = true;
  501.       blockNSWB.checked = true;
  502.     } else {
  503.       blockNSWB.disabled = false;
  504.     }
  505.   },
  506.   
  507.   buttonToTitle: function(btid) {
  508.     return "NoScript - " + $(btid).getAttribute("label");
  509.   }
  510.  
  511. }
  512.  
  513. var ABE = ns.__parent__.ABE;
  514.  
  515. var abeOpts = {
  516.   selectedRS: null,
  517.   _map: {},
  518.   
  519.   init: function() {
  520.     
  521.     if (!(ABE.legacySupport || ABE.__parent__.Thread.canSpin)) {
  522.       var tab = $("nsopt-tabABE");
  523.       if (tab.selected) {
  524.         tab.parentNode.selectedIndex = 0;
  525.       }
  526.       tab.hidden = true;
  527.       return;
  528.     }
  529.     
  530.     this.list = $("abeRulesets-list");
  531.     ABE.updateRules();
  532.     this.populate();
  533.     window.addEventListener("focus", function(ev) {
  534.       if (ABE.updateRulesNow()) abeOpts.populate();
  535.     }, false);
  536.   },
  537.   
  538.   _populating: false,
  539.   populate: function() {
  540.     this._populating = true;
  541.     try {
  542.       this._map = {};
  543.       var l = this.list;
  544.       for(var j = l.getRowCount(); j-- > 0; l.removeItemAt(j));
  545.       var rulesets = ABE.rulesets;
  546.       var selItem = null;
  547.       if (rulesets) {
  548.         var sel = this.selectedRS && this.selectedRS.name;
  549.         this.selectedRS = null;
  550.         var i, name;
  551.         for each (var rs in rulesets) {
  552.           name = rs.name;
  553.           this._map[name] = rs;
  554.           i = l.appendItem(name, name);
  555.           if (rs.disabled) i.setAttribute("disabled", "true");
  556.           if (sel == name) selItem = i;
  557.           if (rs.errors) i.className = "noscript-error";
  558.         }
  559.       }
  560.       l.selectedItem = selItem;
  561.       this.sync();
  562.     } finally {
  563.       this._populating = false;
  564.     }
  565.   },
  566.   
  567.   selected: function(i) {
  568.     if (!this._populating) this.sync();
  569.   },
  570.   
  571.   select: function(rs) {
  572.     var name = rs && rs.name;
  573.     if (!name) return;
  574.     var l = this.list;
  575.     if (l.selectedItem && l.selectedItem.value == name) return;
  576.     
  577.     for(var j = l.getRowCount(), i; j-- > 0;) {
  578.       i = l.getItemAtIndex(j);
  579.       if (i.value == name) {
  580.         l.selectedItem = i;
  581.         break;
  582.       }
  583.     }
  584.   },
  585.   
  586.   sync: function() {
  587.     var selItem = this.list.selectedItem;
  588.    
  589.     var rs = null;
  590.     if (selItem) {
  591.       this.selectedRS = rs = this._map[selItem.value];
  592.     } else {
  593.       this.selectedRS = null;
  594.     }
  595.     
  596.     $("abeEnable-button").disabled = ! ($("abeDisable-button").disabled = !rs || rs.disabled);
  597.     $("abeEdit-button").disabled = !rs || rs.site;
  598.     $("abeRefresh-button").disabled = this.list.getRowCount() == 0;
  599.     
  600.     var text = $("abeRuleset-text");
  601.     text.className = selItem && selItem.className || '';
  602.     text.disabled = !selItem || selItem.disabled;
  603.     text.value = rs && (rs.errors && rs.errors.join("\n\n") || rs.source) || '';
  604.   },
  605.   
  606.   refresh: function() {
  607.     ABE.refresh();
  608.     this.populate();
  609.   },
  610.   
  611.   toggle: function(enabled) {
  612.     var selItem = this.list.selectedItem;
  613.     var rs = this.selectedRS;
  614.     if (!(rs && selItem && rs.name == selItem.value)) return;
  615.     if ((rs.disabled = !enabled)) {
  616.       selItem.setAttribute("disabled", "true");
  617.     } else {
  618.       selItem.removeAttribute("disabled");
  619.     }
  620.     ns.setPref("ABE.disabledRulesetNames", ABE.disabledRulesetNames);
  621.     this.sync();
  622.   },
  623.   
  624.   edit: function(i) {
  625.     i = i || this.list.selectedItem;
  626.     if (!i) return;
  627.     var file = ABE.getRulesetFile(i.value);
  628.     if (!(file instanceof CI.nsILocalFile)) return;
  629.    
  630.     try {
  631.       file.launch();
  632.       return;
  633.     } catch(e) {
  634.       // probably a *X platform...
  635.     }
  636.     
  637.     var ed = this.editor;
  638.     if (!ed) return;
  639.  
  640.     var mimeInfoService = CC["@mozilla.org/uriloader/external-helper-app-service;1"]
  641.         .getService(CI.nsIMIMEService);
  642.     var mimeInfo = mimeInfoService
  643.       .getFromTypeAndExtension( "application/x-abe-rules", "abe" );
  644.     mimeInfo.preferredAction = mimeInfo.useHelperApp;
  645.     
  646.     if ("nsILocalHandlerApp" in CI) {
  647.       var handler =  CC["@mozilla.org/uriloader/local-handler-app;1"].createInstance(CI.nsILocalHandlerApp);
  648.       handler.executable = ed;
  649.       ed = handler;
  650.     }
  651.     mimeInfo.preferredApplicationHandler = ed;
  652.     mimeInfo.launchWithFile(file);      
  653.   
  654.   },
  655.   
  656.   get editor() {
  657.     var ed = null;
  658.     try {
  659.       ed = ns.prefs.getComplexValue("abe.editor", CI.nsILocalFile);
  660.       ed.followLinks = true;
  661.       if (ed.exists() && ed.isExecutable()) return ed;
  662.       ed = null;
  663.     } catch(e) {}
  664.     const IFP = CI.nsIFilePicker;
  665.     const fp = CC["@mozilla.org/filepicker;1"].createInstance(IFP);
  666.       
  667.     fp.init(window, ns.getString("abe.chooseEditor"), IFP.modeOpen);
  668.     fp.appendFilters(IFP.filterApps);
  669.     fp.filterIndex = 0;
  670.     const ret = fp.show();
  671.     if (ret == IFP.returnOK) {
  672.       ed = fp.file;
  673.       if (ed.exists() && ed.isExecutable()) {  
  674.         ns.prefs.setComplexValue("abe.editor", CI.nsILocalFile, ed);
  675.       } else ed = null;
  676.     }
  677.     return ed;
  678.   }
  679.   
  680. }
  681.  
  682.  
  683.